
package rcpsp;

import data.Data;
import data.Job;
import ilog.concert.IloException;
import ilog.concert.IloLinearNumExpr;
import ilog.concert.IloNumVar;
import ilog.cplex.IloCplex;
import java.util.ArrayList;

// Model with conflicts
public class ModelConflicts extends Model {
    
    public ModelConflicts(IloCplex cplex, Data data){
        super(cplex, data);
    }
    
    
    IloNumVar[][] y;
    
    @Override
    public void createModel() throws IloException
    {
        
        y = new IloNumVar[data.getNbJobs()+2][data.getHorizon()];
        
        for(int j = 0 ; j < data.getNbJobs() + 2 ; j++)
        {
            this.y[j] = cplex.boolVarArray(data.getHorizon());
        }
        
        IloLinearNumExpr fobj = cplex.linearNumExpr();
        //sum_{t∈T} t·y_{n+1,t}
        for(int t = 0 ; t < data.getHorizon() ; t++)
        {
            fobj.addTerm(t, y[data.getNbJobs()+1][t]);
        }
        
        cplex.addMinimize(fobj);
        //sum_{t=0}^T y_{it} = 1, ∀i ∈ V
        for(int i = 0 ; i < data.getNbJobs() + 2 ; i++)
        {
            IloLinearNumExpr ct1 = cplex.linearNumExpr();
            for(int t = 0 ; t < data.getHorizon() ; t++)
            {
                ct1.addTerm(1, y[i][t]);
            }
            cplex.addEq(ct1, 1);
        }
        //sum_{t=0}^T t·(y_{jt} − y_{it}) ≥ p_i , ∀(i, j) ∈ E
        for(Job i : data.getJobs())
        {
            for(Job j : i.getSuccessors())
            {
                IloLinearNumExpr ct21 = cplex.linearNumExpr();
                IloLinearNumExpr ct22 = cplex.linearNumExpr();
                for(int t = 0 ; t < data.getHorizon() ; t++)
                {
                    ct21.addTerm((double)t, y[data.getJobs().indexOf(j)][t]);
                    ct22.addTerm((double)t, y[data.getJobs().indexOf(i)][t]);
                }
                cplex.addGe(cplex.diff(ct21, ct22), data.getJobs().get(data.getJobs().indexOf(i)).getProcessTime());
            }
        }
        //sum_{i∈V} b_{ik} sum_{r=t−P_i+1}^t y_{ir} ≤ B_k , ∀k ∈ R, ∀t = 1,...,T
        for(int k = 0 ; k < data.getNbRess() ; k++)
        {
            for(int t = 0 ; t < data.getHorizon() ; t++)
            {
                IloLinearNumExpr ct3 = cplex.linearNumExpr();
                for(Job i : data.getJobs())
                {
                    IloLinearNumExpr ct31 = cplex.linearNumExpr();
                    for(int r = t - i.getProcessTime() + 1 ; r <= t ; r++)
                    {
                        if(r >= 0)
                        {
                            ct31.addTerm(i.getAmountRequestRess().get(k).doubleValue(), y[data.getJobs().indexOf(i)][r]);
                        }
                    }            
                    ct3.add(ct31);
                }
                cplex.addLe(ct3, data.getAvailableRess().get(k));
            }
        }
        
        
    } 

    public IloNumVar[][] getY() {
        return y;
    }
    
    public void getS() throws IloException{
        int i=0;
        for (IloNumVar[] yi:y) {
            int t=0;
            for(IloNumVar yit:yi){
                if(cplex.getValue(yit)!=0.0){
                    System.out.println("S" + i + " = " + t);
                }
                t++;
            }
            i++;
        }
    }

    @Override
    public long solve() throws IloException {
        long timeToSolve = System.nanoTime();
        ArrayList<Integer> startsJobs = new ArrayList<>();        
        
        if(!cplex.solve()) throw new IloException("No optimal Solution found");
                
        int i=0;
        for (IloNumVar[] yi:y) {
            int t=0;
            for(IloNumVar yit:yi){
                if(cplex.getValue(yit)!=0.0){
                    startsJobs.add(t);
                }
                t++;
            }
            i++;
        }
        
        timeToSolve = (System.nanoTime() - timeToSolve) / 1000000;
        
        result = new Solution(startsJobs, (int)Math.round(cplex.getObjValue()), timeToSolve);
        
        return timeToSolve;
    }

    @Override
    public Solution getResult() {
        return result;
    }
    
}
